//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Text;
using ER = Microsoft.Research.DataLayer;

namespace Microsoft.Research.DataLayer
{
    /// <summary>
    /// 
    /// </summary>
    public interface ISearchGroup
    {
    }

    /// <summary>
    /// 
    /// </summary>
    public interface ISearchGroupUnary
    {
        /// <summary>
        /// 
        /// </summary>
        ISearchClause P1 { get; }

        /// <summary>
        /// 
        /// </summary>
        SearchRelUnary OP { get; }
    }

    /// <summary>
    /// 
    /// </summary>
    public interface ISearchGroupBinary
    {
        /// <summary>
        /// 
        /// </summary>
        ISearchClause P1 { get; }

        /// <summary>
        /// 
        /// </summary>
        SearchRelBinary OP { get; }

        /// <summary>
        /// 
        /// </summary>
        ISearchClause P2 { get; }
    }


    /// <summary>
    /// Represents an unary group (such as NOT)
    /// </summary>
    [Serializable]
    public abstract class SearchGroupUnary : ISearchGroupUnary, ISearchGroup, ISearchClause
    {
        private ISearchClause p1;
        private SearchRelUnary op;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="op"></param>
        /// <param name="p1"></param>
        public SearchGroupUnary(SearchRelUnary op, ISearchClause p1)
        {
            this.p1 = p1;
            this.op = op;
        }

        #region ISearchGroup Members

        /// <summary>
        /// Predicate
        /// </summary>
        public ER.ISearchClause P1
        {
            get { return p1; }
        }

        /// <summary>
        /// Operation
        /// </summary>
        public SearchRelUnary OP
        {
            get { return op; }
        }

        #endregion
    }

    /// <summary>
    /// Represents a binary operation, such as AND or OR
    /// </summary>
    [Serializable]
    public abstract class SearchGroupBinary : ISearchGroupBinary, ISearchGroup, ISearchClause
    {
        private ISearchClause p1;
        private ISearchClause p2;
        private SearchRelBinary op;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="op"></param>
        /// <param name="p2"></param>
        protected SearchGroupBinary(ISearchClause p1, SearchRelBinary op, ISearchClause p2)
        {
            this.p1 = p1;
            this.p2 = p2;
            this.op = op;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="op"></param>
        /// <param name="pArray"></param>
        protected SearchGroupBinary(SearchRelBinary op, params ISearchClause[] pArray)
        {
            System.Diagnostics.Debug.Assert(!(pArray == null || pArray.Length < 2));
            if (pArray == null || pArray.Length < 2)
                throw new ArgumentException("Binary condition needs at least 2 expressions");

            ISearchClause[] p1Array = AllocClauseArray(pArray.Length / 2 + pArray.Length % 2);
            ISearchClause[] p2Array = AllocClauseArray(pArray.Length / 2);

            Array.Copy(pArray, 0, p1Array, 0, p1Array.Length);
            Array.Copy(pArray, p1Array.Length, p2Array, 0, p2Array.Length);

            if (p1Array.Length == 1)
                this.p1 = p1Array[0];
            else
                this.p1 = CreateSearchGroup(op, p1Array);

            this.op = op;

            if (p2Array.Length == 1)
                this.p2 = p2Array[0];
            else
                this.p2 = CreateSearchGroup(op, p2Array);
        }


        /// <summary>
        /// Creates a search group of this kind
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="op"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        protected abstract SearchGroupBinary CreateSearchGroup(ISearchClause p1, SearchRelBinary op, ISearchClause p2);

        /// <summary>
        /// Creates a search group of this kind
        /// </summary>
        /// <param name="op"></param>
        /// <param name="pArray"></param>
        /// <returns></returns>
        protected abstract SearchGroupBinary CreateSearchGroup(SearchRelBinary op, params ISearchClause[] pArray);

        /// <summary>
        /// Allocates an array of clauses
        /// </summary>
        /// <param name="size"></param>
        /// <returns></returns>
        protected abstract ISearchClause[] AllocClauseArray(int size);

        #region ISearchGroup Members

        /// <summary>
        /// First predicate
        /// </summary>
        public ER.ISearchClause P1
        {
            get { return p1; }
        }

        /// <summary>
        /// Second predicate
        /// </summary>
        public ER.ISearchClause P2
        {
            get { return p2; }
        }

        /// <summary>
        /// Operation
        /// </summary>
        public SearchRelBinary OP
        {
            get { return op; }
        }

        #endregion
    }
}
